package org.ccay.excel.importer.service.impl.internal;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.ccay.core.io.CompressScheme;
import org.ccay.core.io.FileStoreType;
import org.ccay.core.io.VFSFileObject;
import org.ccay.core.util.FileUtil;
import org.ccay.core.util.StreamUtil;
import org.ccay.core.util.StringUtil;
import org.ccay.core.xml.BigXmlWriteHandler;
import org.ccay.core.xml.XmlHelper;
import org.ccay.excel.importer.ExcelCellError;
import org.ccay.excel.importer.IExcelImportContext;
import org.ccay.excel.meta.SheetMeta;
import org.ccay.excel.util.impl.ExcelUtil;
import org.ccay.vfs.ApacheVFSFileObject;
import org.ccay.vfs.CompressApacheVFSFileObject;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Namespace;
import org.dom4j.QName;
import org.xml.sax.SAXException;

/**
 * 错误信息追加文件。 Created：2013-8-6
 * 
 * @author chaowangbang
 * @version:
 */
public class ExcelXMLFileAppender {
	
	/**
	 * 工作文件夹
	 */
	private VFSFileObject workFolderFileObject;
	
	/**
	 * excel的上下文
	 */
	private IExcelImportContext context;

	/**
	 * sheet数据的文件解压目录
	 */
	public static final String STREAM_DATA_FILEPATH = "xl/worksheets/";

	/**
	 * sheet的对应xml文件 需要解析
	 */
	public static final String STREAM_DATA_SHEETNAME = "xl/workbook.xml";
	
	/**
	 * sheet的对应xml文件 需要解析
	 */
	public static final String STREAM_DATA_SHEETPATH = "xl/_rels/workbook.xml.rels";


	/**
	 * sheet对应的数据xml文件名称
	 */
	public Map<String, String> sheetXmlName = new HashMap<String, String>();

	public Set<String> sheetNameSet = new HashSet<String>();
	
	/**
	 * 临时解压文件
	 */
	public static String tempDirPath;

	public static String[] xpath;

	public String sheetName;
	
	public String excelSheetName;

	public String errorDisplay;
	/**
	 * sheet的错误数据信息
	 */
	public Map<String, StringBuffer> sheetErrorData = new HashMap<String, StringBuffer>();

	ExcelXmlAppenderHandler xmlReadHandler = null;
	BigXmlWriteHandler xmlWriteHandler = null;
	Element rootElment = XmlHelper.createElement("worksheet");

	/**
	 * 构建操作对象
	 * 
	 * @param writeFile
	 * @param context
	 * @param errorDisplay
	 */
	ExcelXMLFileAppender(IExcelImportContext context,String errorDisplay) {
		
		this.context = context;
		this.errorDisplay = errorDisplay;
		//错误信息追加以归档文件为基础
		VFSFileObject importFileObject = new ApacheVFSFileObject(ExcelUtil.VFS_MODULE_EXCEL,context.getRequest().getFilePath(),FileStoreType.ARCHIVE);
		//解压文件到工作目录
		CompressApacheVFSFileObject compressFile = new CompressApacheVFSFileObject(importFileObject,"",CompressScheme.zip);
		workFolderFileObject = compressFile.copyToWork();
		
//		this.writeFile = context.getRequest().getFilePath().replace(".jlr", ".err");
//		// 构建一个临时目录使用
//		tempDirPath = MessageFormat.format("{0}/{1}",
//				PathUtil.getTempPathByDay(context.getRequest().getExcelType()),
//				UUID.randomUUID().toString());

	}

	/**
	 * 开始处理的方法 Created：2013-8-6
	 * 
	 * @author chaowangbang
	 * @throws IOException 
	 * @throws SAXException 
	 * @throws DocumentException 
	 */
	public String appenderErrorColDate() throws IOException, SAXException, DocumentException {
		// 并读出sheet的对应的文件
		decompressXmlFile();
		// 组装错误数据
		buildExcelErrorColDate();
		//添加解析数据校验功能 
		xpath = new String[] { "/worksheet/dimension", "/worksheet/sheetViews",
				"/worksheet/sheetFormatPr", "/worksheet/cols",
				"/worksheet/sheetData/row","/worksheet/hyperlinks", "/worksheet/phoneticPr",
				"/worksheet/pageMargins", "/worksheet/pageSetup", "/worksheet/dataValidations"};
		Set<String> passSheetName = new HashSet<String>();
		String sheetFileName = null;
		/**
		 * 迭代处理sheet
		 */
		for (SheetMeta sheet : context.getExcelMeta().getSheetList()) {
			sheetName = sheet.getSheetName();
			excelSheetName = context.getNameDisplayNameMap().get(sheet.getPartSheetName());
			// 如果已经执行过 或者 当前sheet没有错误信息 那么不执行
			if (passSheetName.contains(excelSheetName)
					|| !sheetNameSet.contains(excelSheetName)) {
				continue;
			}
			passSheetName.add(excelSheetName);
			sheetFileName = sheetXmlName.get(excelSheetName);
			if (StringUtil.isNullOrEmpty(sheetFileName)) {
				continue;
			}
			// 最后一个null参数 是构造给错误列以后扩展使用的
			startRead(sheetFileName, sheet.getTitleRowIndex(), sheet.getErrorDisplayRow());
		}
		String zipFilePath = workFolderFileObject.getLocalAbsolutePath().substring(0, workFolderFileObject.getLocalAbsolutePath().length() - 1)+".err";
		//压缩文件
		FileUtil.createZipFile(workFolderFileObject.getLocalAbsolutePath(),zipFilePath);
		//归档
		VFSFileObject zipVFSfile = new ApacheVFSFileObject(ExcelUtil.VFS_MODULE_EXCEL,
				workFolderFileObject.getRelativePath().substring(0, workFolderFileObject.getRelativePath().length() - 1) +".err", FileStoreType.WROK);
		VFSFileObject archiveErr = zipVFSfile.copyToArchive();
		String archivePath = archiveErr.getRelativePath();
		//清理工作文件夹
		workFolderFileObject.deleteAll();
		workFolderFileObject.close();
		//清理压缩文件夹
		zipVFSfile.delete();
		zipVFSfile.close();
		//关闭归档文件
		archiveErr.close();
		//返回错误信息路径
		return archivePath;
		
	}

	public void startRead(String sheetFileName, int titleRowIndex, String addColNo) throws IOException, SAXException {
		Document doc = null;
		ApacheVFSFileObject sheetFileRead;
		ApacheVFSFileObject sheetFileWrite;
		InputStream inputStream = null;
		try {
			sheetFileRead = new ApacheVFSFileObject(ExcelUtil.VFS_MODULE_EXCEL,
					workFolderFileObject.getRelativePath() + STREAM_DATA_FILEPATH + sheetFileName, FileStoreType.WROK);
			sheetFileWrite = new ApacheVFSFileObject(ExcelUtil.VFS_MODULE_EXCEL,
					workFolderFileObject.getRelativePath() + STREAM_DATA_FILEPATH + sheetFileName+ ".bak", FileStoreType.WROK);
			inputStream = sheetFileRead.getInputStream();
			xmlWriteHandler = new BigXmlWriteHandler(sheetFileWrite.getOutputStream());
			rootElment
			.addAttribute("xmlns:r",
					"http://schemas.openxmlformats.org/officeDocument/2006/relationships");
			//回写内容头命名空间 
			Namespace namespace = new Namespace("", "http://schemas.openxmlformats.org/spreadsheetml/2006/main");
			QName qname = new QName("xmlns", namespace);
			rootElment.addAttribute(qname, "");
			// 创建一个xml文档，写入内容，并保存
			xmlWriteHandler.startDocument();
			xmlWriteHandler.writeOpen(rootElment);// 写入根元素开始标签
			xmlReadHandler = new ExcelXmlAppenderHandler(inputStream, xpath, xmlWriteHandler);
			xmlReadHandler.setTitleRowIndex(titleRowIndex);
			xmlReadHandler.setAddColNo(addColNo);
			xmlReadHandler.setErrorDisplay(errorDisplay);
			xmlReadHandler.setSheetErrorData(sheetErrorData);
			xmlReadHandler.setSheetName(excelSheetName);
			doc = xmlReadHandler.startRead();
			xmlWriteHandler.writeClose(rootElment);// 写入根元素结束标签
			xmlWriteHandler.endDocument();
		} finally {
			if(doc!=null){
				doc.clearContent();
				doc = null;
			}
			if(inputStream != null){
				inputStream.close();//关闭输入流
				inputStream = null;
			}
			
			xmlWriteHandler.close();// 关闭写出流
			
		}
		//用带有错误提示的Sheet替换旧sheet
		sheetFileWrite.getFileObject().moveTo(sheetFileRead.getFileObject());
		sheetFileRead.close(); //关闭读文件
		sheetFileWrite.close();//关闭写文件
	}

	/**
	 * 构建错误信息 Created：2013-8-6
	 * 
	 * @author chaowangbang
	 */
	public void buildExcelErrorColDate() {
		List<ExcelCellError> errorsData = context.getErrors();
		String tempSheetkey = null;
		StringBuffer msg = null;
		for (ExcelCellError error : errorsData) {
			sheetNameSet.add(error.getSheetName());
			tempSheetkey = error.getSheetName() + "|-|" + error.getRowIndex();
			msg = sheetErrorData.get(tempSheetkey);
			if (null == msg) {
				msg = new StringBuffer(error.toErrorMessage());
				sheetErrorData.put(tempSheetkey, msg);
			} else {
				msg.append(error.toErrorMessage());
			}
		}
	}

	/**
	 * 解析xml文件中的sheet对应关系
	 * 
	 * @throws IOException
	 *             Created：2017-10-13
	 * @throws DocumentException
	 */
	public void decompressXmlFile() throws IOException, DocumentException {
		Document doc = null;
		ApacheVFSFileObject workbook = null;
		InputStream inputStream = null;
		try{
			workbook = new ApacheVFSFileObject(ExcelUtil.VFS_MODULE_EXCEL,
					workFolderFileObject.getRelativePath() + STREAM_DATA_SHEETNAME, FileStoreType.WROK);
			inputStream = workbook.getInputStream();
			doc = XmlHelper.readDocument(inputStream);
			Element rootElement = doc.getRootElement();
			Element sheetsElement = null;
			Element sheetElement = null;
			if (rootElement != null) {
				for (Iterator<?> itr = rootElement.elementIterator(); itr.hasNext();) {
					sheetsElement = (Element) itr.next();
					if ("sheets".equals(sheetsElement.getName())) {
						for (Iterator<?> itrSheet = sheetsElement.elementIterator(); itrSheet
								.hasNext();) {
							sheetElement = (Element) itrSheet.next();
							sheetXmlName.put(sheetElement.attributeValue("name"), getSheetPath(sheetElement.attributeValue("id")));
						}
						break;
					}
				}
			}
		}finally {
			if(inputStream != null){
				StreamUtil.closeStreams(inputStream);
			}
			if(workbook !=null){
				workbook.close();
			}
			doc = null;
		}
	}
	
	
	/** 
	 * 增加根据rid从引用XML文件获取文件路径
	 * @author chaowangbang
	 * @since 2016年6月8日
	 * @param rid
	 * @return
	 * @throws DocumentException
	 */
	public String getSheetPath(String rid) throws DocumentException {
		String sheetPath = "";
		if (rid == null) {
			return sheetPath;
		}
		Document doc = null;
		doc = XmlHelper.readDocument(workFolderFileObject.getLocalAbsolutePath() + STREAM_DATA_SHEETPATH);
		Element rootElement = doc.getRootElement();
		Element relationshipElement = null;
		if (rootElement != null) {
			for (Iterator<?> itr = rootElement.elementIterator(); itr.hasNext();) {
				relationshipElement  = (Element) itr.next();
				String id = relationshipElement.attributeValue("Id");
				if (rid.equals(id)) {
					sheetPath = relationshipElement.attributeValue("Target").replace("worksheets/", "");
					return sheetPath;
				}
			}
		}
		return sheetPath;
	}
}